<?php
namespace WDB\Validation;
use WDB,
    WDB\Exception;

/**
 * @author Richard Ejem <richard(at)ejem.cz>
 * @package WDB
 */
class UniqueConstraint implements iValidator
{
    /**@var array*/
    protected $uniqueKey;
    /**@var string*/
    protected $name;


    //WDB\Event\iEventListener implementation
    public function raise(WDB\Wrapper\iRecord $record = NULL) //validate passed record
    {
        if ($record === NULL) throw new Exception\BadArgument("Validator event raise() method needs"
            . " WDB\Wrapper\iRecord as a raise argument");

        if (!method_exists($record->getTable(), 'getDatasource'))
        {
            throw new Exception\WDBException('record`s table does not have a datasource');
        }
        $key = array();
        foreach ($this->uniqueKey as $col)
        {
            if (is_array($col))
            {
                $recName = $col['recName'];
                $ci = WDB\GTO\WsqlLanguage::quoteIdentifier($col['ColumnIdentifier']);
            }
            else
            {
                $recName = $ci = $col;
            }
            $key[$ci] = $record[$recName];
            if ($key[$ci] === NULL) return true; //null values can be multiple or an about-to-be-generated value occured
        }

        $utest = $record->getTable()->getDatasource()->filter($key);

        if (!$record->isNew())
        {
            $utest = $utest->not(method_exists($record, 'getUniqueIdentificationKey') ? $record->getUniqueIdentificationKey() : $record->getIdentificationKey());
        }

        if ($utest->count())
        {
            $columns = $record->getTable()->getColumns();
            if (count($this->uniqueKey) == 1)
            {
                $record->getField($recName)->addValidationError($message=str_replace('%name', $columns[$recName]->getTitle(), WDB\Lang::s('validator.messages.uniqueViolated')));
                $record->addValidationErrors(array(array('message'=>$message, 'column'=>$columns[$recName])));
            }
            else
            {
                $titles = array();
                foreach ($this->uniqueKey as $col)
                {
                    if (is_array($col)) $col = $col['recName'];
                    $titles[] = $columns[$col]->getTitle();
                }
                $record->addValidationErrors(array(str_replace('%fieldlist', implode(', ', $titles), WDB\Lang::s('validator.messages.uniqueGroupViolated'))));
            }
            return false;
        }
        else
        {
            return true;
        }
    }
    //WDB\Event\iEventListener end

    //iValidator implementation
    /**
     * @throws WDB\Exception\WDBException
     */
    public function fetchValidatorData(WDB\Wrapper\iRecord $record, &$data)
    {
        if (!isset($data['UniqueConstraints']))
        {
            $data['UniqueConstraints'] = array();
        }
        if (isset($data['UniqueConstraints'][$this->name])) throw new Exception\WDBException("Duplicate constraint name: {$this->name}");
        $data['UniqueConstraints'][$this->name] = $this->uniqueKey;
    }
    //iValidator end

    /**
     * @param string constraint name
     * @param string|array column or group of columns to be unique
     */
    public function __construct($name, $key)
    {
        if (is_string($key)) $key = array($key);
        if (!is_array($key)) throw new Exception\BadArgument('$key must be sting or array');
        $this->uniqueKey =  $key;
        $this->name = $name;
    }
}
